package pl.edu.mimuw.irs.rcp.controller.editor;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.tools.SelectionTool;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.DeleteAction;
import org.eclipse.gef.ui.actions.EditorPartAction;
import org.eclipse.gef.ui.actions.RedoAction;
import org.eclipse.gef.ui.actions.SaveAction;
import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.gef.ui.actions.StackAction;
import org.eclipse.gef.ui.actions.UndoAction;
import org.eclipse.gef.ui.actions.UpdateAction;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;

import pl.edu.mimuw.irs.core.model.design.IRSWorkflow;
import pl.edu.mimuw.irs.core.util.persistence.IPersistenceManager;
import pl.edu.mimuw.irs.core.util.persistence.PersistenceAdapter;
import pl.edu.mimuw.irs.rcp.controller.factory.EditPartFactory;
import pl.edu.mimuw.irs.rcp.controller.listener.TextTransferDropTargetListener;
import pl.edu.mimuw.irs.rcp.controller.menu.WorkflowContextMenuProvider;
import pl.edu.mimuw.irs.rcp.util.Logger;
import pl.edu.mimuw.irs.rcp.util.MessageCode;
import pl.edu.mimuw.irs.rcp.util.UITexts;

/**
 * @author Jakub Rauch
 * Created on: 2009-05-17
 */
public class IRSWorkflowEditor extends EditorPart implements IWorkflowEditor {
	
	// *** Static attributes
	public static final String ID = "pl.edu.mimuw.irs.editor.irsworkflow";

	// *** Attributes
	private IRSWorkflow model;
	private EditDomain editDomain;
	public boolean dirty;
	private CommandStackListener commandStackListener = new CommandStackListener() {
		public void commandStackChanged(EventObject event) {
			//updateActions(stackActionIDs);
			setDirty(getCommandStack().isDirty());
		}
	};
	private ISelectionListener selectionListener = new ISelectionListener() {
		public void selectionChanged(IWorkbenchPart part, ISelection selection) {
			updateActions(editPartActionIDs);
		}
	};
	private GraphicalViewer graphicalViewer;
	private EditPartFactory editPartFactory;
	private ActionRegistry actionRegistry;
	private List<String> editPartActionIDs = new ArrayList<String>();
	private List<String> stackActionIDs = new ArrayList<String>();
	private List<String> editorActionIDs = new ArrayList<String>();
	// Getters and setters
	public IRSWorkflow getModel() {
		return model;
	}
	protected EditDomain getEditDomain() {
		if (editDomain == null) editDomain = new DefaultEditDomain(this);
		return editDomain;
	}
	public CommandStack getCommandStack() {
		return getEditDomain().getCommandStack();
	}
	protected CommandStackListener getCommandStackListener() {
		return commandStackListener;
	}
	public GraphicalViewer getGraphicalViewer() {
		return graphicalViewer;
	}
	public EditPartFactory getEditPartFactory() {
		if (editPartFactory == null) editPartFactory = new EditPartFactory();
		return editPartFactory;
	}
	public ActionRegistry getActionRegistry() {
		if (actionRegistry == null) actionRegistry = new ActionRegistry();
		return actionRegistry;
	}
	protected ISelectionListener getSelectionListener() {
		return selectionListener;
	}
	protected void addEditPartAction(SelectionAction action) {
		getActionRegistry().registerAction(action);
		editPartActionIDs.add(action.getId());
	}
	protected void addStackAction(StackAction action) {
		getActionRegistry().registerAction(action);
		stackActionIDs.add(action.getId());
	}
	protected void addEditorAction(EditorPartAction action) {
		getActionRegistry().registerAction(action);
		editorActionIDs.add(action.getId());
	}
	protected void addAction(IAction action)
	{
		getActionRegistry().registerAction(action);
	}
	
	// *** Constructors
	
	public IRSWorkflowEditor() {
		//setEditDomain(new DefaultEditDomain(this));
		getEditDomain().setActiveTool(new SelectionTool());
	}
	
	// *** Methods


	public void createPartControl(Composite parent){
		graphicalViewer = createGraphicalViewer(parent);
	}
	
	@Override
	public void setFocus() {
		
	}
	
	@Override
	public void doSave(IProgressMonitor monitor) {
		IEditorInput editorInput = getEditorInput();
		
		monitor.beginTask(UITexts.get(MessageCode.WORKFLOW_SAVE_PROGRESS), IProgressMonitor.UNKNOWN);
		
		if (editorInput instanceof FileEditorInput) {
			FileEditorInput fileEditorInput = (FileEditorInput) editorInput;
			
			IPersistenceManager manager = PersistenceAdapter.getManager(IRSWorkflow.class);
			InputStream source;
			try {
				source = manager.objectAsInputStream(getModel());
				fileEditorInput.getFile().setContents(source, IResource.FORCE, null);
			} catch (CoreException e) {
				Logger.handle(MessageCode.WORKFLOW_SAVE_FILE_PROBLEM, e);
				return;
			} catch (Exception e) {
				Logger.handle(MessageCode.WORKFLOW_SAVE_SERIALIZE_PROBLEM, e);
				return;
			}
		} else {
			Logger.handle(MessageCode.WORKFLOW_SAVE_INPUT_PROBLEM, editorInput.getClass(), editorInput.getName());
			return;
		}
		
		monitor.done();
		
		// update CommandStack
		getCommandStack().markSaveLocation();
	}
	
	@Override
	public void doSaveAs() {
		// XXX : save as

		// update CommandStack
		getCommandStack().markSaveLocation();
	}
	
	@Override
	public boolean isDirty() {
		return dirty;
	}
	
	protected void setDirty(boolean dirty) {
		if(this.dirty != dirty) {
			this.dirty = dirty;
			firePropertyChange(IEditorPart.PROP_DIRTY);
		}
	}
	
	@Override
	public boolean isSaveAsAllowed() {
		// XXX : is save allowed...
		return true;
	}

	public void gotoMarker(IMarker marker) {
		// XXX gotoMarker? czy to w ogóle potrzebne?
	}
	
	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
		// store site and input
		setSite(site);
		setInput(input);
		// add CommandStackListener
		getCommandStack().addCommandStackListener(getCommandStackListener());
		
		// add selection change listener
		getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(
				getSelectionListener());
		
		
		
		createActions();
	}
	
	@Override
	public void dispose() {
		// remove CommandStackListener
		getCommandStack().removeCommandStackListener(getCommandStackListener());
		// remove selection listener
		getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(
				getSelectionListener());
		// delete action registry
		getActionRegistry().dispose();
		
		super.dispose();
	}
	
	protected void createActions()
	{
		addStackAction(new UndoAction(this));
		addStackAction(new RedoAction(this));
		
		addEditPartAction(new DeleteAction((IWorkbenchPart) this));
		
		addEditorAction(new SaveAction(this));
		//addEditorAction(new PrintAction(this));
	}
	
	protected GraphicalViewer createGraphicalViewer(Composite parent) {
		
		GraphicalViewer viewer = new ScrollingGraphicalViewer();
		viewer.createControl(parent);
		
		// configure the viewer
		//viewer.getControl().setBackground(parent.getBackground());
		viewer.getControl().setBackground(new Color(null, 255,255,255));

		viewer.setRootEditPart(new ScalableFreeformRootEditPart());
		// hook the viewer into the EditDomain
		getEditDomain().addViewer(viewer);
		// ??? getGraphicalViewer().setEditDomain(new DefaultEditDomain(this));
		
		// activate the viewer as selection provider for Eclipse
		getSite().setSelectionProvider(viewer);
		// initialize the viewer with input
		viewer.setEditPartFactory(getEditPartFactory());
		viewer.setContents(getModel());
		
		// add context menu
		ContextMenuProvider contextMenuProvider = new WorkflowContextMenuProvider(viewer, this);
		viewer.setContextMenu(contextMenuProvider);

		getSite().registerContextMenu(contextMenuProvider, viewer);
		
		// drag'n'drop handling
		viewer.addDropTargetListener(new TextTransferDropTargetListener(viewer));
		
		return viewer;
	}
	
	@Override
	protected void setInput(IEditorInput input) {
		super.setInput(input);
		
		IRSWorkflow workflow = null;

		try {
			InputStream stream = ((FileEditorInput)input).getStorage().getContents();
			IPersistenceManager manager = PersistenceAdapter.getManager(IRSWorkflow.class);
			workflow = (IRSWorkflow)manager.loadFromStream(stream);
		} catch (CoreException e) {
			Logger.handle(MessageCode.WORKFLOW_LOAD_DESERIALIZE_PROBLEM, e, input.getName());
			return;
		} catch (Exception e) {
			Logger.handle(MessageCode.WORKFLOW_LOAD_DESERIALIZE_PROBLEM, e, input.getName());
			return;
		}
		
		model = workflow;
		
		// TODO : UndoablePropertySheetEntry
		// TODO : ScrollableThumbnail
		// TODO : MouseWheelZoomHandler
	}
	
	private void updateActions(List<String> actionIDs) {
		for (String id : actionIDs) {
			IAction action = getActionRegistry().getAction(id);
			if (action != null && action instanceof UpdateAction)
				((UpdateAction) action).update();
		}
	}
	
	@SuppressWarnings("unchecked")
	public Object getAdapter(Class adapter) {
		// we need to handle common GEF elements we created
		if (adapter == GraphicalViewer.class || adapter == EditPartViewer.class) {
			return getGraphicalViewer();
		} else if (adapter == CommandStack.class) {
			return getCommandStack();
		} else if (adapter == EditDomain.class) {
			return getEditDomain();
		} else if (adapter == ActionRegistry.class) {
			return getActionRegistry();
		} else {
			// the super implementation handles the rest
			return super.getAdapter(adapter);
		}
	}
	
	protected void firePropertyChange(int propertyId) {
		super.firePropertyChange(propertyId);
		updateActions(stackActionIDs);
		updateActions(editorActionIDs);
		// TODO : poprawic obsluge undo/redo, dodac delete, save itp
	}
}
